#include "parse_api.h"
#include "basics/error_handle.h"
#include "init/init.h"

#include "netns.h"
#include <fcntl.h>
#include <sys/ioctl.h>

#include "cgroup/cg.h"
#include "persistence/persistence.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wordexp.h>

static int netns_ip = 1;
/// int cntr_create(const char *image_path, const char *command, char *argv[]);
void parse_create(const int client_socket) {
    uid_t uid;
    gid_t gid;
    size_t image_path_len, command_len;
    check(recv(client_socket, &uid, sizeof(uid_t), 0) != -1);
    check(recv(client_socket, &gid, sizeof(gid_t), 0) != -1);
    check(recv(client_socket, &image_path_len, sizeof(size_t), 0) != -1);
    char image_path[image_path_len];
    check(recv(client_socket, image_path, sizeof(image_path), 0) != -1);
    check(recv(client_socket, &command_len, sizeof(size_t), 0) != -1);
    char command[command_len];
    check(recv(client_socket, command, sizeof(command), 0) != -1);
    // int container_id;
    int pipe_fd[2];
    check(pipe(pipe_fd) != -1);

    int pipe_fd_child[2];
    check(pipe(pipe_fd_child) != -1);
    /// fork a process to deal with it, the server will not be blocked
    netns_ip++;
    if (fork() == 0) {
        // /// ↓ this will let child not reaped by parent not to be zombie
        // process check(setsid() != -1);
        wordexp_t words;
        check(close(pipe_fd_child[0]) != -1);
        check(wordexp(command, &words, 0) == 0);
        char *exec_file = words.we_wordv[0];
        check(setgid(gid) != -1);
        check(setuid(uid) != -1);
        int container_id = cntr_create(image_path, exec_file, words.we_wordv,
                                       pipe_fd_child[1], netns_ip);
        /// avoid mem leak
        wordfree(&words);
        check(write(pipe_fd[1], &container_id, sizeof(int)) != -1);
        check(close(pipe_fd[1]) != -1);

        check(send(client_socket, &container_id, sizeof(int), 0) != -1);
        exit(0);
    }

    int container_id;
    check(close(pipe_fd[1]) != -1);
    check(read(pipe_fd[0], &container_id, sizeof(int)) != -1);
    check(close(pipe_fd[0]) != -1);
    // create  a sub cgroup for the new container
    int rt = create_cgroup(container_id);
    check(rt == 0);
    struct metadata meta_data;
    meta_data.conatiner_id = container_id;
    load_metadata(&meta_data);
    apply_to_cgroup(container_id, meta_data.root_pid);

    pid_t child_pid;
    check(close(pipe_fd_child[1]) != -1);
    check(read(pipe_fd_child[0], &child_pid, sizeof(pid_t)) != -1);
    create_netns_host_veth(child_pid, netns_ip);
    check(close(pipe_fd_child[0]) != -1);
    check(waitpid(-1, NULL, WNOHANG) != -1);
}

void parse_attach(const int client_socket) {
    uid_t uid;
    gid_t gid;
    pid_t pgid;
    int container_id;
    size_t tty_len, command_len;
    check(recv(client_socket, &uid, sizeof(uid_t), 0) != -1);
    check(recv(client_socket, &gid, sizeof(gid_t), 0) != -1);
    check(recv(client_socket, &pgid, sizeof(pid_t), 0) != -1);
    check(recv(client_socket, &tty_len, sizeof(size_t), 0) != -1);
    char tty[tty_len];
    check(recv(client_socket, tty, sizeof(tty), 0) != -1);
    check(recv(client_socket, &container_id, sizeof(int), 0) != -1);
    check(recv(client_socket, &command_len, sizeof(size_t), 0) != -1);
    char command[command_len];
    check(recv(client_socket, command, sizeof(command), 0) != -1);
    /// fork a process to deal with it, the server will not be blocked
    if (fork() == 0) {
        wordexp_t words;
        check(wordexp(command, &words, 0) == 0);
        char *exec_file = words.we_wordv[0];
        int rt;
        check(recv(client_socket, &rt, sizeof(int), 0) != -1);
        check(setsid() != -1);
        check(close(STDIN_FILENO) != -1);
        check(close(STDOUT_FILENO) != -1);
        check(close(STDERR_FILENO) != -1);
        check(openat(STDIN_FILENO, tty, O_RDONLY, S_IRWXU) != -1);
        check(openat(STDOUT_FILENO, tty, O_WRONLY | O_APPEND, S_IRWXU) != -1);
        check(openat(STDERR_FILENO, tty, O_WRONLY | O_APPEND, S_IRWXU) != -1);
        check(ioctl(STDIN_FILENO, TIOCSCTTY, 1) != -1);
        rt = cntr_attach(container_id, exec_file, words.we_wordv);
        /// avoid mem leak
        wordfree(&words);
        check(ioctl(STDIN_FILENO, TIOCNOTTY) != -1);
        check(send(client_socket, &rt, sizeof(int), 0) != -1);
        exit(0);
    }
    check(waitpid(-1, NULL, WNOHANG) != -1);
}

void parse_delete(const int client_socket) {
    int container_id;
    check(recv(client_socket, &container_id, sizeof(int), 0) != -1);
    pid_t pid;
    if ((pid = fork()) == 0) {
        int rt = cntr_delete(container_id);
        check(send(client_socket, &rt, sizeof(int), 0) != -1);
        int del_cg_rt = delete_cgroup(container_id);
        check(del_cg_rt == 0);
        exit(0);
    }
    check(waitpid(-1, NULL, WNOHANG) != -1);
}

void parse_update(const int client_socket) {
    int container_id;
    char update_type;
    unsigned int update_value;
    check(recv(client_socket, &container_id, sizeof(int), 0) != -1);
    check(recv(client_socket, &update_type, sizeof(char), 0) != -1);
    check(recv(client_socket, &update_value, sizeof(unsigned int), 0) != -1);

    // check(update_type=='c');
    // printf("[update]%d %c %d\n",container_id,update_type,update_value);

    pid_t pid;
    int rt;
    int pipe_fd[2];
    check(pipe(pipe_fd) != -1);
    if ((pid = fork()) == 0) {
        check(close(pipe_fd[0]) != -1);
        switch (update_type) {
        case 'm':
            rt = modify_mem_limit(container_id, update_value);
            break;
        case 'c':
            rt = modify_cpu_share(container_id, update_value);
            break;
        case 'n':
            rt = modify_cpus(container_id, update_value);
            break;
        default:
            rt = -2;
        }
        // printf("[update] result %d\n",rt);
        check(write(pipe_fd[1], &rt, sizeof(int)) != -1);
        check(close(pipe_fd[1]) != -1);
        exit(0);
    }
    check(close(pipe_fd[1]) != -1);
    check(read(pipe_fd[0], &rt, sizeof(int)) != -1);
    check(close(pipe_fd[0]) != -1);
    check(send(client_socket, &rt, sizeof(int), 0) != -1);
}
